package com.self.console.proxy;

import com.self.console.constant.Constant;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.entity.DeflateDecompressingEntity;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Random;


/**
 * 代理访问
 * @author jackey.wang 2011-9-27 下午12:45:02
 * @version 3.0
 */
public class ProxyHttpClient {

	private final static Logger LOG = LoggerFactory.getLogger(ProxyHttpClient.class);

	enum ContentEncoding {
		GZIP, // zip压缩内容
		DEFLATE, // deflate压缩
		UNZIP; // 非压缩内容
	}

	/**
	 * 代理访问,随机遍历代理服务器
	 * @param proxyList
	 * @param url
	 * @param charset
	 * @return RequestResult
	 * @throws IOException
	 */
	public static RequestResult httpProxyRequest(List<ProxyServer> proxyList, String url, String charset) {
		RequestResult result = null;
		if (proxyList == null || proxyList.isEmpty()) {
			result = new RequestResult();
			result.setReturnCode(Constant.ResultCode.PROXY_NULL.getValue());
			result.setContent(Constant.ResultCode.PROXY_NULL.getName());
			return result;
		}

		RandomGet<ProxyServer> rg = new RandomGet<ProxyServer>(proxyList);
		ProxyServer proxyServer = null;
		int i = 0;
		while ((proxyServer = rg.get()) != null ) {
			try {
				result = httpClientRequest(proxyServer, url, charset);
			} catch (Exception e) {
				if (LOG.isWarnEnabled())
					LOG.warn("使用代理访问页面异常！URL：" + url + "，代理IP：" + proxyServer.getIp() + "，端口：" + proxyServer.getPort(), e);
			}
			if (result != null && result.getReturnCode() == HttpURLConnection.HTTP_OK) {
				return result;
			}
			if (i ++ > 9) {
				if (result == null) {
					result = new RequestResult();
					result.setReturnCode(Constant.ResultCode.PROXY_EXCEED.getValue());
					result.setContent(Constant.ResultCode.PROXY_EXCEED.getName());
				}
				break;
			}
		}
		return result;
	}

	private static RequestResult httpClientRequest(ProxyServer proxyServer, String url, String charset) {
		Long start = System.currentTimeMillis();
		url = url.replace("|", "%7C");
		DefaultHttpClient httpClient = null;
		try {
			int index1 = url.indexOf("//") + 2;
			int index2 = url.lastIndexOf("/");
			String host = "";
			String postfix = "";
			if (index2 > index1) {
				host = url.substring(url.indexOf("//") + 2, url.lastIndexOf("/"));
				postfix = url.substring(url.lastIndexOf("/"), url.length());
			} else {
				host = url;
			}
			httpClient = new DefaultHttpClient();
			httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
			httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);

			httpClient.getCredentialsProvider().setCredentials(
					new AuthScope(proxyServer.getIp(), proxyServer.getPort()), 
					new UsernamePasswordCredentials("", ""));

			HttpHost targetHost = new HttpHost(host);
			HttpHost proxy = new HttpHost(proxyServer.getIp(), proxyServer.getPort()); // 这两句话去掉就是直接访问不用代理
			httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
			
			HttpGet httpGet =null;
			if(postfix.contains(".") && (postfix.substring(0,postfix.lastIndexOf(".")-1)).contains(".") || url.contains("images.blemall")){
				URL urls = new URL(targetHost+postfix);
				java.net.URI uri = new java.net.URI(urls.getProtocol(), urls.getHost(), urls.getPath(), urls.getQuery(), null);
				httpGet = new HttpGet(uri);
			}else{
				httpGet = new HttpGet(postfix);
			}
			RequestResult result = null;
			try {
			  addCommonHeader(httpGet, postfix);
              HttpResponse response = httpClient.execute(targetHost, httpGet);
              result = reponse(url, response, charset);
            } catch (Exception e) {
              e.printStackTrace();
            } finally {
              httpGet.abort();
              httpGet = null;
            }
			if (LOG.isDebugEnabled())
				LOG.debug("代理访问，代理IP：{}，端口：{}，请求URL：{}，耗时：{}ms", 
						new Object[]{proxyServer.getIp(), proxyServer.getPort(), url, System.currentTimeMillis() - start});
			return result;
			
		} catch (Exception e) {
			if (LOG.isDebugEnabled())
				LOG.warn("httpProxyRequest is error，url：" + url, e);
		} finally {
			if (httpClient != null) {
				httpClient.getConnectionManager().shutdown();
			}
		}
		return null;
	}

	/**
	 * 服务器直接访问
	 * @param url
	 * @param charset
	 * @return RequestResult
	 */
	public static RequestResult httpRequest(String url, String charset) {
		DefaultHttpClient httpClient = null;
		url = url.replace("|", "%7C");
		try {
			httpClient = new DefaultHttpClient();
			httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000);
			httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000);
			
			HttpGet httpGet =null;
			if(url.contains("paipaiimg") || url.contains("images.blemall")){
				URL urls = new URL(url);
				java.net.URI uri = new java.net.URI(urls.getProtocol(), urls.getHost(), urls.getPath(), urls.getQuery(), null);
				httpGet = new HttpGet(uri);
			}else{
				httpGet = new HttpGet(url);
			}
			RequestResult result = null;
			try {
			  addCommonHeader(httpGet, url);
	          HttpResponse response = httpClient.execute(httpGet);
	          result = reponse(url, response, charset);
            } catch (Exception e) {
              e.printStackTrace();
            } finally {
              httpGet.abort();
              httpGet = null;
            }
			return result;
		} catch (Exception e) {
			if (LOG.isWarnEnabled())
				LOG.warn("httpRequest is error，url：" + url, e);
		} finally {
			if (httpClient != null) {
				httpClient.getConnectionManager().shutdown();
			}
		}
		return null;
	}

	private static RequestResult reponse(String url, HttpResponse response, String charset) throws Exception {
		RequestResult result = new RequestResult();
		result.setReturnCode(response.getStatusLine().getStatusCode());
		if (response.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) { // 成功返回
			HttpEntity entity = response.getEntity();

			String contentEncoding = entity.getContentEncoding() != null
					&& entity.getContentEncoding().getValue() != null ? entity.getContentEncoding().getValue() : "";
			String contentType = entity.getContentType().getValue();
			String content = null;
			result.setContentType(contentType);

			if (contentEncoding.equalsIgnoreCase(ContentEncoding.GZIP.name())) {
				entity = new GzipDecompressingEntity(response.getEntity());
			} else if (contentEncoding.equalsIgnoreCase(ContentEncoding.DEFLATE.name())) {
				entity = new DeflateDecompressingEntity(response.getEntity());
			}

			//添加条件:url.contains("/fzjy/ccpm/"),是为判断成交持仓排名网的处理
			if (result.isBinaryStream() || url.contains("/fzjy/ccpm/")) {
				result.setStream(readStream(entity.getContent()));
			} else {
				content = EntityUtils.toString(entity, charset);
				Document doc = Jsoup.parse(content);
				Elements eles = doc.select("meta[http-equiv=Content-Type]");
				if (eles != null && !eles.isEmpty()) {
					Element e = eles.get(0);
					String v = e.attr("content");
					String set;
					if (v.indexOf("charset") > -1) {
						set = v.substring(v.indexOf("=") + 1, v.length());
					} else {
						Elements eles0 = doc.select("meta[charset]");
						Element e0 = eles0.get(0);
						set = e0.attr("charset");
					}
					if (!charset.toLowerCase().equals(set.toLowerCase())) {
						content = httpRequest(url, set).getContent();
					}
				} else {
					if (LOG.isDebugEnabled())
						LOG.debug("not has char set url：{}", url);
				}
			}
			try {
				EntityUtils.consume(response.getEntity());
			} catch (Exception e) {
				if (LOG.isDebugEnabled())
					LOG.warn("visit Exception url:" + url, e);
			}
			result.setContent(content);
			return result;
		} else {
			LOG.warn("visit url:{}, return code:{}", url, response.getStatusLine().getStatusCode());
		}
		return result;
	}

	public static byte[] readStream(InputStream stream) throws IOException {
		ByteArrayOutputStream outsStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[10 * 1024];
		int len = 0;
		while ((len = stream.read(buffer)) != -1) {
			outsStream.write(buffer, 0, len);
		}
		outsStream.close();
		stream.close();
		return outsStream.toByteArray();
	}

	private static void addCommonHeader(HttpRequest httpRequest, String url) {
		httpRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36");
		httpRequest.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
		httpRequest.addHeader("Accept-Language", "zh-CN,zh;q=0.8");
		httpRequest.addHeader("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");
		httpRequest.addHeader("Cache-Control", "max-age=0");
		httpRequest.addHeader("Accept-Encoding", "gzip, deflate, sdch");
		if (url.contains("fx678.com")) { // 此网站访问列表页面，必须携带cookie，变态
			httpRequest.addHeader("Cookie", "ASPSESSIONIDSSTRCDQQ=LJNKFMAALOHODCJFDADAHCHI,Hm_lpvt_d25bd1db5bca2537d34deae7edca67d3=1342781335293,lzstat_ss=145101891_0_1342810125_884719");
		}
	}

	/**
	 * 添加HttpURLConnection参数
	 */
	public static void addRequestProperty(HttpURLConnection connection) {
		connection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        connection.setRequestProperty("Referer", "http://www.google.com.hk/search?q=%E9%87%91%E4%BB%B7&hl=zh-CN&newwindow=1&safe=strict&prmd=n&source=lnms&tbs=nws:1&ei=OdXXTJjwNcLXcbvZudQL&sa=X&oi=mode_link&ct=mode&ved=0CBIQ_AU");
        connection.setRequestProperty("Accept-Language", "zh-cn,zh;q=0.5");
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12");
        connection.setRequestProperty("Accept-Encoding", "deflate"); 
        connection.setRequestProperty("Host", "www.google.com.hk");
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Cookie", "PREF=ID=2e0a54044d759c49:U=2407b9c611506060:FF=2:LD=zh-CN:NW=1:TM=1310115253:LM=1310115329:S=omPe_BKd8ucxU6lo; NID=48=N1ORlo1okwuZ2Xs5VKBPwIZ9t7U31t6Mz8lONe7FeOTqH50m7IflhETt-66t0XsH1A_X-BQ-kn4NJlCMcwHnojSkR6QiTa8JoL9YoszTU7qwHY3nfVsbSrv_YFuv3prV");
	}
	
	/**
	 * 随机检索 给定的list，并返回list中的元素，保证list中的每个位置最多被检索一次。<br/>
	 * 检索次数超出list的size后返回null
	 * @author Teng
	 * @param <T>
	 */
	public static class RandomGet<T> {
		private List<T> list;
		private int[] seed;
		private int i;
		Random random = new Random();
		RandomGet(List<T> list) {
			if (list == null || list.isEmpty()) {
				throw new IllegalArgumentException("list不能为NULL或者空。");
			}

			this.list = list;
			seed = new int[list.size()];
			for (int k = 0; k < list.size(); k++) {
				seed[k] = k;
			}
		}
		public T get(){
			T server = null;
			if (i < seed.length) {
				int j = random.nextInt(seed.length - i);
				server = list.get(seed[j]);
				seed[j] = seed[seed.length - 1 - i];
				i ++;
			}
			return server;
		}
	}
}
